package com.springsource.a11AspectAndAdvisor;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;

public class AspectAndAdvisor {

    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("aspect1", Aspect1.class);
        context.registerBean("config", Config.class);
        context.registerBean("configurationPostProcessor", ConfigurationClassPostProcessor.class);
        // 是一个BeanPostProcessor，在依赖注入之前、初始化之后作用，创建代理
        Class<AnnotationAwareAspectJAutoProxyCreator> creatorClass = AnnotationAwareAspectJAutoProxyCreator.class;
        context.registerBean(creatorClass);


        context.refresh();
        // AnnotationAwareAspectJAutoProxyCreator
        // 第一个重要的方法findEligibleAdvisors：找到“有资格”的Advisors
        // a.一部分是低级的，可以由自己编写，如下例中的advisor3
        // b.另一部分是高级的，解析@Aspect后获得
        AnnotationAwareAspectJAutoProxyCreator proxyCreator = context.getBean(creatorClass);
//        proxyCreator.findEligibleAdvisors(Target1.class, "target1");
        Method findEligibleAdvisors = creatorClass.getSuperclass().getSuperclass().getDeclaredMethod("findEligibleAdvisors", Class.class, String.class);
        findEligibleAdvisors.setAccessible(true);
        List<Advisor> advisorList = (List<Advisor>) findEligibleAdvisors.invoke(proxyCreator, Target1.class, "target1");
//        for (Advisor advisor : advisorList) {
//            System.out.println(advisor);
//        }


        // 第二个重要的方法 wrapIfNecessary，它内部调用findEligibleAdvisors，只要返回的集合不为空，则表示需要创建代理
        Method wrapIfNecessaryMethod = creatorClass.getSuperclass().getSuperclass().getSuperclass().getDeclaredMethod("wrapIfNecessary", Object.class, String.class, Object.class);
        wrapIfNecessaryMethod.setAccessible(true);
        Object o1 = wrapIfNecessaryMethod.invoke(proxyCreator, new Target1(), "target1", "target1");
        Object o2 = wrapIfNecessaryMethod.invoke(proxyCreator, new Target2(), "target1", "target1");
        System.out.println(o1.getClass());
        System.out.println(o2.getClass());
        ((Target1) o1).sayHello();

    }





    static class Target1{
        public void sayHello(){
            System.out.println("Target1 hello");
        }

    }


    static class Target2{

        public void sayGoodbye(){
            System.out.println("Target2 goodbye");
        }
    }

    @Aspect
    static class Aspect1 {
        @Before("execution(* sayHello())")
        public void before(){
            System.out.println("Aspect1 before");
        }

        @After("execution(* sayHello())")
        public void after(){
            System.out.println("Aspect1 after");
        }
    }


    @Configuration
    static class Config {

        @Bean
        public Advisor advisor3(){
            AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
            pointcut.setExpression("execution(* sayHello())");
            return new DefaultPointcutAdvisor(pointcut, new MethodInterceptor() {
                @Override
                public Object invoke(MethodInvocation invocation) throws Throwable {
                    System.out.println("before");
                    Object result = invocation.proceed();
                    System.out.println("after");
                    return result;
                }
            });
        }


    }
}
